from threading import Thread
import ImageFilter, ImageOps, Image, ImageEnhance
from uuid import uuid4
from defines import Defines
from math import ceil
import gtk.gdk
from StringIO import StringIO
from time import time
from scavenger import Scavenger
from scavenger_common import RemoteDataHandle
from common import imageops

class ImageWorker(Thread):
    def __init__(self, image, operations, monitor, outfile = None):
        """
        Constructor.
        @type image: Image
        @param image: The image that the operations are to be applied to.
        @type operations: [(str, obj)]
        @param operations: The operations that are to be applied to the image.
        @type monitor: TaskMonitor
        @param monitor: The progress monitor that wants to be updated whenever 
                        a service has been performed.
        @type outfile: str
        @param outfile: The (optional) file to store the resulting image in.
        """
        Thread.__init__(self)

        # Set member vars.
        self.image = image
        self.operations = operations
        self.monitor = monitor
        self.outfile = outfile

        # Set thread properties.
        self.setDaemon(True)
        
        # Register this task with the task monitor.
        self.taskid = self.monitor.add_task()
        
    # def ops_to_task(self, ops):
    #         elif name == 'picasa':
    #             arguments[argument_name_prefix + '_uname'] = arg[0]
    #             arguments[argument_name_prefix + '_passwd'] = arg[1]
    #             arguments[argument_name_prefix + '_album'] = arg[2]
    #             arguments[argument_name_prefix + '_title'] = arg[3]
    #             xml = '''
    #             <service id="service%s"
    #              serviceName="daimi.imaging.picasa_upload@1">
    #                  <parameters>
    #                      <value id="image" %s />
    #                      <value id="username" class="input" valueId="%s_uname" />
    #                      <value id="password" class="input" valueId="%s_passwd" />
    #                      <value id="album" class="input" valueId="%s_album" />
    #                      <value id="title" class="input" valueId="%s_title" />
    #                  </parameters>
    #             </service>
    #             '''%(service_id, image_argument, argument_name_prefix, argument_name_prefix, 
    #                  argument_name_prefix, argument_name_prefix)
    #         elif name == 'mail':
    #             arguments[argument_name_prefix + '_smtp_server'] = arg[0]
    #             arguments[argument_name_prefix + '_smtp_port'] = arg[1]
    #             arguments[argument_name_prefix + '_sender_address'] = arg[2]
    #             arguments[argument_name_prefix + '_smtp_username'] = arg[3]
    #             arguments[argument_name_prefix + '_smtp_password'] = arg[4]
    #             arguments[argument_name_prefix + '_recipient_address'] = arg[5]
    #             arguments[argument_name_prefix + '_subject'] = arg[6]
    #             arguments[argument_name_prefix + '_message'] = ''
    #             arguments[argument_name_prefix + '_mime_type'] = arg[7]
    #             xml = '''
    #             <service id="service%s"
    #              serviceName="daimi.communication.send_mail@1">
    #                  <parameters>
    #                      <value id="attachment" %s />
    #                      <value id="server" class="input" valueId="%s_smtp_server" />
    #                      <value id="port" class="input" valueId="%s_smtp_port" />
    #                      <value id="sender" class="input" valueId="%s_sender_address" />
    #                      <value id="username" class="input" valueId="%s_smtp_username" />
    #                      <value id="password" class="input" valueId="%s_smtp_password" />
    #                      <value id="recipient" class="input" valueId="%s_recipient_address" />
    #                      <value id="subject" class="input" valueId="%s_subject" />
    #                      <value id="message" class="input" valueId="%s_message" />
    #                      <value id="mime_type" class="input" valueId="%s_mime_type" />
    #                  </parameters>
    #             </service>
    #             '''%(service_id, image_argument, argument_name_prefix, argument_name_prefix,
    #                  argument_name_prefix, argument_name_prefix, argument_name_prefix,
    #                  argument_name_prefix, argument_name_prefix, argument_name_prefix,
    #                  argument_name_prefix)
    #        
    #         output.append(xml)
    #     
    #     
    #     out = '''
    #         <output>
    #             <value id="image" class="result" sourceId="service%s" />
    #         </output>
    #     '''%service_id
    #     
    #     output.append(out)
    #     
    #     output.append('</task>')
    #     
    #     return ("".join(output), arguments)
        
    def __optimize_ops(self):
        # Set the op-count that is used for the progress indication.
        self.opcount = len(self.operations)
        self.step = 0
        
    def __progress(self):
        if self.step == 0:
            return 0
        else:
            progress = float(self.step) / self.opcount * 100
            return int(ceil(progress))
    
    def update_task_status(self):
        gtk.gdk.threads_enter()
        self.step += 1
        try:
            self.monitor.update_task(self.taskid, self.operations[self.step][0], self.__progress())
        except:
            pass
        gtk.gdk.threads_leave()

    def run(self):
        # Send start notification.
        self.monitor.program.popup_message('Initiated task %i.'%(self.taskid))
        
        # Optimize the operations.
        self.__optimize_ops()
        
        # Start timing the operation.
        start_time = time()

        # Convert the Image into jpeg for transferring to the surrogate.
        sio = StringIO()
        self.image.save(sio, 'jpeg') 

        # Start performing the operations.
        image = sio.getvalue()
        name = ''
        try:
            for (name, arg) in self.operations:
                if name == 'sharpen':
                    image = imageops.sharpen(image)
                elif name == 'blur':
                    image = imageops.blur(image)
                elif name == 'invert':
                    image = imageops.invert(image)
                elif name == 'brightness':
                    image = imageops.brightness(image, arg)
                elif name == 'color':
                    image = imageops.color(image, arg)
                elif name == 'contrast':
                    image = imageops.contrast(image, arg)
                elif name == 'rotate':
                    image = imageops.rotate(image, arg)
                elif name == 'flip_vertical':
                    image = imageops.flip_vertical(image)
                elif name == 'flip_horizontal':
                    image = imageops.flip_horizontal(image)
                elif name == 'scale':
                    image = imageops.scale(image, arg)
                elif name == 'picasa':
                    imageops.picasa_upload(image, *arg)
                elif name == 'mail':
                    imageops.send_mail(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], image, 'image/jpeg')
                self.update_task_status()

        except Exception, e:
            gtk.gdk.threads_enter()
            self.monitor.mark_task_error(self.taskid, 'Error applying %s operation.'%name)
            gtk.gdk.threads_leave()
            # Send a notification.
            self.monitor.program.popup_message('Task %i failed.\n%s'%(self.taskid, e.args[0]))
            return             
            
        # Fetch the resulting file - if needed.
        if self.outfile and type(image) == RemoteDataHandle:
            try:
                image = Scavenger.fetch_data(image)
            except Exception, e:
                gtk.gdk.threads_enter()
                self.monitor.mark_task_error(self.taskid, 'Error fetching file from surrogate.')
                gtk.gdk.threads_leave()
                # Send a notification.
                self.monitor.program.popup_message('Task %i failed.\n%s'%(self.taskid, e.args[0]))
                return
            
        end_time = time()
                                
        if self.outfile:
            # Convert the resulting data into an image.
            sio = StringIO(image)
            self.image = Image.open(sio)

            # Validate the outfile name.
            index = self.outfile.rfind('.')
            if index == -1 or len(self.outfile[index+1:]) != 3:
                # There is no extension - add one.
                self.outfile += '.jpg'
            elif not (self.outfile[index+1:] in ('jpg','gif')):
                # The format is unsupported. Switch to jpg.
                self.outfile = self.outfile[:index] + '.jpg'
            
            # Save the image to the local file system.
            try:
                self.image.save(self.outfile)
            except Exception, e:
                gtk.gdk.threads_enter()
                self.monitor.mark_task_error(self.taskid, 'Error saving file.')
                gtk.gdk.threads_leave()
                # Send a notification.
                self.monitor.program.popup_message('Output from task %i could not be written to file.\nErrMsg: %s'%(self.taskid, e.message))
                return

        # Update the progress indicator.
        gtk.gdk.threads_enter()
        self.monitor.update_task(self.taskid, 'Processed in %.1f seconds'%(end_time-start_time), 100)
        gtk.gdk.threads_leave()

        # Mark the task as done/inactive.
        gtk.gdk.threads_enter()
        self.monitor.remove_task(self.taskid)
        gtk.gdk.threads_leave()
        
        # Send end notification.
        if self.operations[-1][0] == 'picasa':
            self.monitor.program.popup_message('%s successfully sent to Picasa.'%(self.monitor.program.image_browser.current_image()))
        elif self.operations[-1][0] == 'mail':
            self.monitor.program.popup_message('%s sent to %s'%((self.monitor.program.image_browser.current_image()),
                                                                 self.operations[-1][1][5]))
        elif self.outfile:
            self.monitor.program.popup_message('%s successfully written.'%(self.outfile[self.outfile.rfind('/')+1:]))
                        